﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Qas.CommonLogic;
using Qas.CommonLogic.BusinessObjects;
using System.Windows.Forms;
using System.Reflection;
using System.Runtime.InteropServices;

namespace Qas.User.Keepers
{
    public class BasicK : AbstractKeeper
    {
        // Первое значение
        public static long beginTickFirst;
        public static double firstResult;
        public static bool measureValue = false;
        private long endTickFirst, freqFirst;
        
        // Флаг для выбора алгоритма
        public static string flagALG;

        List<Task> zeroPreCondTasks = new List<Task>();

        private bool forIndex = false;
        private bool flagAdd = false;
        
        // для в глубину
        private bool IsUsedSmallPre = false;
       

        // обратный
        private bool IsUsedSmallPost = false;
        
        List<SearchStruct> planSuspect = new List<SearchStruct>();

        List<Condition> PreGoalConditions = new List<Condition>();
        private static int maxLength = 0;

        // для в ширину
        List<Task> Globaltasks = new List<Task>();
        bool QWasZero = false;
        List<Condition> SpecCond = new List<Condition>();
        bool SpecFlag = true;

        // Импортируем функции из dll
        [DllImport("KERNEL32")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);

        // Конструктор
        public BasicK(Base currentBase) 
            : base(currentBase) {
                this.avaiableBudget = 9999999;
        }

        /// <summary>
        /// Поиск плана
        /// </summary>
        public override List<Plan> Search(List<Task> tasks, List<Condition> initConditions, 
                                            List<Condition> goalConditions)
        {
            // Инициализация переменных
            this.initialState = initConditions;
            this.goalState = goalConditions;

            Log.currentState = initConditions;
            
            List<Plan> results = new List<Plan>();
            List<Task> taskTODel = new List<Task>();

            List<Plan> searchResult = new List<Plan>();
            //int counter = 0;

            this.Globaltasks = tasks;
            
            // Поиск выбранного алгоритма
            if (flagALG == "DepthFirstSearch")
            {
                if (initialState.Count != 0)
                {
                    foreach (Task task in tasks)
                    {
                        if (task.PreConditions.Count == 0)
                        {
                            taskTODel.Add(task);
                            //tasks.Remove(task);
                        }
                    }

                    if (taskTODel.Count != 0)
                    {
                        foreach (Task task in taskTODel)
                        {
                            tasks.Remove(task);
                        }
                    }
                }
                
                searchResult = DepthFirstSearch(new List<Task>(), tasks, initialState,
                                                    results, initialState);

                if (searchResult.Count > 1)
                {
                    FilterDSResult(searchResult);
                    // возврат
                    return searchResult;
                }

                if (Min(initialState) < Min(goalState))
                {
                    if ((searchResult.Count == 0) && (taskTODel.Count != 0))
                    {
                        tasks.AddRange(taskTODel);

                        searchResult = DepthFirstSearch(new List<Task>(), tasks, initialState,
                                                        results, initialState);
                        FilterDSResult(searchResult);
                    }
                }
            }

            if (flagALG == "BreadthFirstSearch")
            {
                if (Min(initialState) < Min(goalState))
                {
                    if (initialState.Count != 0)
                    {
                        foreach (Task task in tasks)
                        {
                            if (task.PreConditions.Count == 0)
                            {
                                taskTODel.Add(task);
                                //tasks.Remove(task);
                            }
                        }

                        if (taskTODel.Count != 0)
                        {
                            foreach (Task task in taskTODel)
                            {
                                tasks.Remove(task);
                            }
                        }
                    }


                    searchResult = BreadthFirstSearch(new List<SearchStruct>(), tasks, initialState,
                                                            results, new List<Task>());
                    
                    if ((searchResult.Count == 0) && (taskTODel.Count != 0))
                    {
                        tasks.AddRange(taskTODel);
                        initialState.Clear();

                        searchResult = BreadthFirstSearch(new List<SearchStruct>(), tasks, initialState,
                                                            results, new List<Task>());
                    }
                }
            }
            
            if (flagALG == "BackwardSearch")
            {
                List<Condition> newInitCond = new List<Condition>();
                List<Task> tToDel = new List<Task>();
                tToDel.AddRange(tasks);

                if ((initConditions.Count == 0))
                {
                    foreach (Task task in tasks)
                    {
                        if (task.PreConditions.Count == 0)
                        {
                            zeroPreCondTasks.Add(task);
                            newInitCond.AddRange(task.PostConditions);
                        }
                    }

                    if (zeroPreCondTasks.Count != 0)
                    {
                        this.initialState = newInitCond;
                    }
                    else
                    {
                        return searchResult;
                    }
                }
                // при использовании цикла IsUsedSmallPost
                // сносим, чтобы не уходить в дебри
                else
                {
                    Condition cSmallest = initConditions[0];

                    foreach (Condition cond in initConditions)
                    {
                        if (cond.ID <= cSmallest.ID)
                        {
                            cSmallest = cond;
                        }
                    }

                    foreach (Task task in tToDel)
                    {
                        if (task.PreConditions.Count == 0)
                        {
                            tasks.Remove(task);
                            continue;
                        }
                        foreach (Condition cond in task.PreConditions)
                        {
                            if (cond.ID < cSmallest.ID)
                            {
                                tasks.Remove(task);
                                break;
                            }
                        }
                    }
                }
                
                searchResult = BackwardSearch(goalConditions, tasks, new List<BSStruct>(), results, 
                                                    new List<Task>(), goalConditions);
                //FilterBSResult(searchResult);
            }
            
            return searchResult;            
        }

        //private void SortBSResult(List<Plan> result)
        //{
        //    Plan p = result[0];

        //    p.Tasks[0].ID
        //}
        //private void FilterBSResult(List<Plan> result)
        //{
        //    if (result.Count > 1)
        //    {
        //        Plan pMax = result[0];

        //        foreach (Plan pl in result)
        //        {
        //            if (pl.Title.Length > pMax.Title.Length)
        //            {
        //                pMax = pl;
        //            }
        //        }

        //        maxLength = pMax.Title.Length;

        //        result.RemoveAll(RemCond);

                
        //    }
        //}

        private static bool RemCond(/*List<Plan> lp,*/ Plan pl)
        {
            if (pl.Title.Length < maxLength)
                return true;

            return false;
        }
        
        private int Min(List<Condition> conds)
        {
            if (conds.Count != 0)
            {
                Condition cond = conds[0];

                foreach (Condition cnd in conds)
                {
                    if (cnd.ID <= cond.ID)
                    {
                        cond = cnd;
                    }
                }

                return cond.ID;
            }
            return 0;
        }

        /// <summary>
        /// Фильтр результатов
        /// </summary>
        private void FilterDSResult(List<Plan> result)
        {
            if (result.Count > 1)
            {
                Plan task = result[0];


                foreach (Plan pl in result)
                {
                    if (pl.Title.Length <= task.Title.Length)
                    {
                        task = pl;
                    }
                }

                Plan pSmallest = new Plan(task.Tasks);

                List<Plan> plan = new List<Plan>();
                plan.AddRange(result);
                plan.Remove(task);

                int from;

                foreach (Plan pl in plan)
                {
                    from = pl.Title.IndexOf(pSmallest.Title);
                    if (from >= 0)
                    {
                        result.Remove(pl);
                    }
                }

                string cuttedStart = pSmallest.Title.Remove(0, 3);

                foreach (Plan pl in plan)
                {
                    from = pl.Title.IndexOf(cuttedStart);
                    if (from >= 0)
                    {
                        result.Remove(pl);
                    }
                }

                string cuttedEnd = pSmallest.Title.Remove(pSmallest.Title.Length - 3, 3);
                foreach (Plan pl in plan)
                {
                    from = pl.Title.IndexOf(cuttedEnd);
                    if (from >= 0)
                    {
                        result.Remove(pl);
                    }
                }
            }
        }

        /// <summary>
        /// Проверка списка результирующих планов на содержание похожих планов
        /// </summary>
        private bool IsContains(List<Plan> res, Plan plan)
        {
            foreach (Plan pl in res)
            {
                if (pl.Title == plan.Title)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Подсчет стоимости набора задач
        /// </summary>
        private double CountCost(List<Task> tasks)
        {
            double sum = 0;

            foreach (Task task in tasks)
            {
                sum += task.PDCost.Expectancy;
            }

            return sum;
        }

        /// <summary>
        /// Время выполнения первого значения
        /// </summary>
        private void FirstValueExec()
        {
            QueryPerformanceCounter(out endTickFirst);
            QueryPerformanceFrequency(out freqFirst);

            firstResult = ((double)endTickFirst - (double)beginTickFirst) / (double)freqFirst;
            measureValue = false;
        }

        /// <summary>
        /// Поиск в глубину
        /// </summary>
        private List<Plan> DepthFirstSearch(List<Task> plan/*Stack<SearchStruct>stack*/ /*List<SearchStruct> listDepth*/, 
            List<Task> otherTasks,List<Condition> pre, List<Plan> results, /*List<Task> taskToAdd*/ List<Condition> smallPre) // int counter
        {
            // ЕСЛИ БУДЕТ ВРЕМЯ!!! 
            // глобальная структура очереди (List + insert с нуля), в этой структуре будет bool переменная (IsVisited)
            // также Pre и SmallPre
            // ЕСЛИ БУДЕТ
            List<Task> newOther = new List<Task>();
            newOther.AddRange(otherTasks);
            IsUsedSmallPre = false;

            // Отбор задач и заполнение стэка
            // если не смогли пройти со small, то bigPre
            foreach (Task task in otherTasks)
            {
                if (TaskCanBeStarted(smallPre, task))
                {
                    IsUsedSmallPre = true;

                    List<Condition> newPre = Condition.Summation(pre, task.PostConditions);

                    List<Condition> newSmallPre = new List<Condition>();// возможно сохранять полный путь состояний 
                    newSmallPre.AddRange(task.PostConditions);

                    newOther.Remove(task);
                    ///   
                    if (goalState.Count == 1)
                    {
                        if (!Contains(goalState, /*newPre*/ newSmallPre))
                        {
                            List<Task> newPlan = new List<Task>();
                            newPlan.AddRange(plan);
                            newPlan.Add(task);

                            DepthFirstSearch(newPlan, newOther, newPre, results, newSmallPre);
                        }
                        else
                        {
                            plan.Add(task);
                            Plan check = new Plan(plan);
                            otherTasks.Remove(task);

                            if (!IsContains(results, check))
                            {
                                if (measureValue)
                                {
                                    if (results.Count == 0)
                                    {
                                        FirstValueExec();
                                    }
                                }

                                results.Add(check);
                            }

                            return results;
                        }
                    }
                    else
                    {
                        if (!Contains(goalState, newPre /*newSmallPre*/))
                        {
                            List<Task> newPlan = new List<Task>();
                            newPlan.AddRange(plan);
                            newPlan.Add(task);

                            DepthFirstSearch(newPlan, newOther, newPre, results, newSmallPre);
                        }
                        else
                        {
                            plan.Add(task);
                            Plan check = new Plan(plan);
                            otherTasks.Remove(task);

                            if (!IsContains(results, check))
                            {
                                if (measureValue)
                                {
                                    if (results.Count == 0)
                                    {
                                        FirstValueExec();
                                    }
                                }

                                results.Add(check);
                            }

                            return results;
                        }
                    }
                }                
            }

            if (!IsUsedSmallPre)
                {
                    // добавить флаг о том, что были в предыдущем цикле
                    //List<Task> substitute = new List<Task>();
                    //substitute.AddRange(copyOfNewOther);

                    foreach (Task task in otherTasks /*substitute*/)
                    {
                        if (TaskCanBeStarted(pre, task))
                        {
                            List<Condition> newPre = Condition.Summation(pre, task.PostConditions);

                            List<Condition> newSmallPre = new List<Condition>();// возможно сохранять полный путь состояний 
                            newSmallPre.AddRange(task.PostConditions);

                            newOther.Remove(task);

                            if (!Contains(goalState, newSmallPre))// возможно newPre
                            {
                                List<Task> newPlan = new List<Task>();
                                newPlan.AddRange(plan);
                                newPlan.Add(task);

                                DepthFirstSearch(newPlan, newOther, newPre, results, newSmallPre);
                            }
                            else
                            {
                                plan.Add(task);
                                Plan check = new Plan(plan);
                                otherTasks.Remove(task);

                                if (!IsContains(results, check))
                                {
                                    if (measureValue)
                                    {
                                        if (results.Count == 0)
                                        {
                                            FirstValueExec();
                                        }
                                    }
                                    
                                    results.Add( check);                                        
                                }

                                return results;
                            }
                        }
                    }
                }
                             
            return results;
        }

        private bool ContainsBFS(List<Condition> conditions, List<Condition> taskCond)
        {
            int i = 0;

            foreach (Condition cond in conditions)
            {
                if (!taskCond.Contains(cond))
                {
                    i++;
                    //     return false;
                }                
            }

            if (i > 0)
            {
                foreach (Condition condToCheck in taskCond)
                {
                    if (PreGoalConditions.Contains(condToCheck))
                    {
                        flagAdd = true;
                        return false;
                    }
                }
                return false;
            }
            return true;

        }
        /// <summary>
        /// Поиск в ширину
        /// </summary>
        private List<Plan> BreadthFirstSearch(List<SearchStruct> queue, List<Task> otherTasks,
            List<Condition> pre, List<Plan> results, List<Task> taskToAdd)// предусловия BFSStruct
        {
            // для задач, где есть вилки (проверить на примере!), а особенно когда вилка в самой последней задаче
            // обычный поиск в ширину не помогает(тупо циклится), пока сделано криво

            List<Task> newOther = new List<Task>();
            newOther.AddRange(otherTasks);
                       
            // Отбор задач и заполнение очереди
            foreach (Task task in otherTasks)
            {
                if (TaskCanBeStarted(pre, task))
                {
                    if (!taskToAdd.Contains(task))
                    {
                        if (!QWasZero)
                        {
                            // возможно нужно будет создавать объекты, а не кидать ссылки
                            newOther.Remove(task);// может стоит исключить из SearchStruct newOther
                            List<Condition> newPre = Condition.Summation(pre, task.PostConditions);
                            taskToAdd.Add(task);
                            queue.Add(new SearchStruct(taskToAdd, newPre, CountCost(taskToAdd), newOther));
                            taskToAdd.Remove(task);
                        }
                        else
                        {
                            newOther.Remove(task);
                            taskToAdd.Add(task);
                           
                            SpecCond.AddRange(task.PostConditions);
                            break;
                            //queue.Add(new SearchStruct(taskToAdd, newPre, CountCost(taskToAdd), newOther));
                            //taskToAdd.Remove(task);
                        }

                        //forIndex = true;
                    }
                    
                }                
            }

            if (QWasZero)
            {
                if (SpecCond.Count != 0)
                {
                    List<Condition> newPre = Condition.Summation(pre, SpecCond);
                    queue.Add(new SearchStruct(taskToAdd, newPre, CountCost(taskToAdd), newOther));

                    SpecCond.Clear();
                    SpecFlag = false;
                }
            }
            //if (forIndex == false)
            //{
            //    newOther.Add(taskToAdd.ElementAt(taskToAdd.Count - 1));
            //}

            // Рекурсивный вызов
            foreach (SearchStruct element in queue)
            {
                if (element.MoneyRequired <= avaiableBudget)
                {
                    if (!Contains(goalState, element.Conditions))
                    {
                        //counter++;
                        forIndex = false;
                        //if (flagAdd)
                        //{
                        //    planSuspect.Add(element);
                        //}
                        flagAdd = false;
                        List<Condition> newPre = new List<Condition>();
                        newPre.AddRange(element.Conditions);

                        List<Task> newtaskToAdd = new List<Task>();
                        newtaskToAdd.AddRange(element.Tasks);

                        List<SearchStruct> newQ = new List<SearchStruct>();
                        newQ.AddRange(queue);
                        newQ.Remove(element);

                        //queue.Remove(element);

                        BreadthFirstSearch(newQ, newOther, newPre, results, newtaskToAdd);
                    }
                    else
                    {
                        //forIndex = false;
                        
                        QWasZero = false;
                        Plan check = new Plan(element.Tasks);

                        if (!IsContains(results, check))
                        {
                            if (measureValue)
                            {
                                if (results.Count == 0)
                                {
                                    FirstValueExec();
                                }
                            }

                            results.Add(check);
                            queue.Remove(element);
                        }

                        return results;
                    }
                }
                else
                {
                    MessageBox.Show("Недостаточно средств"); 
                }
            }

            if (queue.Count == 0)
            {
                if (taskToAdd.Count != 0)
                {
                    if (SpecFlag)
                    {
                        if (!QWasZero)
                        {
                            QWasZero = true;
                        }
                        else
                        {
                            QWasZero = false;
                            return results;
                        }
                        List<Task> fixedOtherTasks = new List<Task>();
                        fixedOtherTasks.AddRange(Globaltasks);

                        foreach (Task tFromToAdd in taskToAdd)
                        {
                            if (fixedOtherTasks.Contains(tFromToAdd))
                            {
                                fixedOtherTasks.Remove(tFromToAdd);
                            }
                        }

                        BreadthFirstSearch(queue, fixedOtherTasks, pre, results, taskToAdd);
                    }
                }
            }

            return results;
        }

        private bool TaskCanCover(List<Condition> postConditions, Task task)
        {
            foreach (Condition cond in postConditions)
            {
                if (!task.PostConditions.Contains(cond))
                {
                    return false;
                }
            }

            return true;           
        }

        /// <summary>
        /// Проверка на возможность существования задачи в контексте данных постусловий
        /// Применяется в BackwardSearch поиске
        /// </summary>
        private bool TaskCanBeStartedPost(List<Condition> postConditions, Task task)
        {
            foreach (Condition cond in postConditions)
            {
                if (task.PostConditions.Contains(cond))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Удаление повторяющихся условий
        /// </summary>
        private List<Condition> Correction(List<Condition> conds)
        {
            List<Condition> newConds = new List<Condition>();
            foreach (Condition cond in conds)
            {
                if (!newConds.Contains(cond))
                {
                    newConds.Add(cond);
                }
            }

            return newConds;
        }

        private Condition Max(List<Condition> conds)
        {
            if (conds.Count > 0)
            {
                Condition Maxcond = conds[0];

                foreach (Condition cond in conds)
                {
                    if (cond.ID > Maxcond.ID)
                    {
                        Maxcond = cond;
                    }
                }
                return Maxcond;
            }
            return null;
        }
        /// <summary>
        /// Обратный поиск
        /// </summary>
        private List<Plan> BackwardSearch(List<Condition> post, List<Task> availableTasks, List<BSStruct> queueBS,
                                            List<Plan> results, List<Task> taskToAdd, List<Condition> smallPost)
        {
            List<Task> newAvailableTasks = new List<Task>();
            newAvailableTasks.AddRange(availableTasks);
            IsUsedSmallPost = false;

            //List<Task> additionalTasks = new List<Task>();

            // Отбор задач и заполнение очереди
            foreach (Task task in availableTasks)
            {
                if (smallPost.Count != 0)
                {
                    if (TaskCanCover(smallPost, task))
                    {
                        IsUsedSmallPost = true;

                        List<Condition> newPost = Condition.Summation(post, task.PreConditions);

                        List<Condition> newSmallPost = new List<Condition>();
                        newSmallPost.AddRange(task.PreConditions);

                        newAvailableTasks.Remove(task);
                        taskToAdd.Add(task);
                        queueBS.Add(new BSStruct(taskToAdd, newPost, CountCost(taskToAdd), newAvailableTasks, newSmallPost));
                        taskToAdd.Remove(task);
                    }
                }
                else
                {
                    return results;
                }
                //if (TaskCanBeStartedPost(/*post*/ smallPost, task))
                //{
                //    IsUsedSmallPost = true;

                //    List<Condition> newPost = Condition.Summation(post, task.PreConditions);

                //    List<Condition> newSmallPost = new List<Condition>();
                //    newSmallPost.AddRange(task.PreConditions);
                //    //newAvailableTasks.Remove(task);       

                //    taskToAdd.Add(task);
                //    queueBS.Add(new SearchStruct(taskToAdd, newPost, CountCost(taskToAdd), newAvailableTasks));
                //    taskToAdd.Remove(task);

                //    //additionalTasks.Add(task);
                //}
            }

            if (!IsUsedSmallPost)
            {
               // List<Condition> ForContain = new List<Condition>();
                
                if (availableTasks.Count != 0)
                {
                    List<Condition> newSmallPost = new List<Condition>();

                    foreach (Task task in availableTasks)
                    {
                        if (TaskCanBeStartedPost(post, task))
                        {
                            //IsUsedSmallPost = true;
                            //ForContain.AddRange(task.PreConditions);
                            //newPost = Condition.Summation(post, task.PreConditions);                        
                            //List<Condition> newPost = Condition.Summation(post, newSmallPost);
                            newSmallPost.AddRange(task.PreConditions);

                            newAvailableTasks.Remove(task);
                            taskToAdd.Add(task);
                            //queueBS.Add(new BSStruct(taskToAdd, newPost, CountCost(taskToAdd), newAvailableTasks, newSmallPost));
                            //taskToAdd.Remove(task);
                        }
                    }

                    List<Condition> newPost = Condition.Summation(post, newSmallPost);

                    List<Condition> nSmallPost = Correction(newSmallPost);

                    // добавим только одно условие post
                    if (newSmallPost.Count > 1)
                    {
                        //for (int i = newSmallPost.Count - 1; i > 0; i--)
                        //{
                            List<Condition> nSmallPostFirst = new List<Condition>();
                            
                       nSmallPostFirst.Add(Max(nSmallPost));
                            queueBS.Add(new BSStruct(taskToAdd, newPost, CountCost(taskToAdd), newAvailableTasks, nSmallPostFirst /*newSmallPost*/));
                        //}                        
                    }
                    else
                    {
                        if (newSmallPost.Count == 0)
                        {
                            return results;
                        }
                        else
                        {
                            queueBS.Add(new BSStruct(taskToAdd, newPost, CountCost(taskToAdd), newAvailableTasks, newSmallPost));
                        }
                    }
                }
            }

            //if (additionalTasks.Count > 1)
            //{
            //    List<Condition> newPost = Condition.Summation(post, additionalTasks);

            //    taskToAdd.AddRange(additionalTasks);
            //    queueBS.Add(new SearchStruct(taskToAdd, newPost, CountCost(taskToAdd), newAvailableTasks));
            //}

            // Рекурсивный вызов
            foreach (BSStruct element in queueBS)
            {
                if (element.MoneyRequired <= avaiableBudget)
                {
                    // Отдельный цикл для задач планирования где не выбрано начальное условие
                    //if (zeroPreCondTasks.Count == 0)
                    //{
                        //if (!Contains(initialState, /*element.Conditions*/ element.SmallCond))
                    if (!Contains(element.SmallCond, initialState/*element.Conditions*/ ))
                        {
                            List<Condition> newPost = new List<Condition>();
                            newPost.AddRange(element.Conditions);

                            List<Task> newtaskToAdd = new List<Task>();
                            newtaskToAdd.AddRange(element.Tasks);

                            List<BSStruct> newQ = new List<BSStruct>();
                            newQ.AddRange(queueBS);
                            newQ.Remove(element);

                            BackwardSearch(newPost, newAvailableTasks /*element.OtherTasks*/, newQ, results, newtaskToAdd, element.SmallCond);
                        }
                        else
                        {
                            Plan check = new Plan(element.Tasks);

                            if (zeroPreCondTasks.Count != 0)
                            {
                                foreach (Task task in zeroPreCondTasks)
                                {
                                    check.AddTask(task);
                                }
                            }

                            if (!IsContains(results, check))
                            {
                                if (measureValue)
                                {
                                    if (results.Count == 0)
                                    {
                                        FirstValueExec();
                                    }
                                }

                                results.Add(check);
                                queueBS.Remove(element);
                            }

                            return results;
                        }
                    //}
                    //else
                    //{
                         
                    //}
                }
                else
                {
                    MessageBox.Show("Недостаточно средств"); 
                }
            }
            
            return results;
        }        
    }
}
